package Model.TransFormModel;
import Model.SurroundModel.Target;
import Model.SurroundModel.TargetSecond;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;

public class Sampling2 {
    public static ArrayList<Target> totalTarget = new ArrayList<>(); //保存二值化图片后的所有点
    public static ArrayList<Target> targets = new ArrayList<>(); //保存采点后的顶点
    public static ArrayList<TargetSecond> targetseconds = new ArrayList<>(); //保存采点后的顶点
    private static int SW = 162; //二值图像的阈值（这里根据测试设置为162，可根据实际图像情况进行更改）
    public int MOVE = 100; //图像坐标平移距离
    private static final double BORDER = 0.95; //设置多少π可以认为两点共线（因为存在误差所以可能不为1）
    private static final double π = Math.PI;

    //默认构造函数
    public Sampling2() {}
    //设定阈值构造函数
    public Sampling2(int MOVE) {
        this.MOVE = MOVE;
    }
    //设定阈值与坐标移动距离的构造函数
    public Sampling2(int SW, int MOVE) {
        this.SW = SW;
        this.MOVE = MOVE;
    }

    //将图片转换为二值图片
    /**
     * 转换
     *
     * @param srcPath   原图片url地址
     * @param alterPath 保存图片url地址
     */
    public void transfer(String srcPath, String alterPath) throws IOException {
        totalTarget.clear();
        BufferedImage bi = ImageIO.read(new File(srcPath));//通过imageio将图像载入
        int h = bi.getHeight();//获取图像的高
        int w = bi.getWidth();//获取图像的宽
        int rgb = bi.getRGB(0, 0);//获取指定坐标的ARGB的像素值
        int[][] gray = new int[w][h];//用于保存每个像素的灰度值
        int num = 0; //用于给目标进行编号

        //循环获取每个像素点的灰度值
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                gray[x][y] = getGray(bi.getRGB(x, y));
            }
        }

        //创建BufferedImage对象保存转换成二值的图像
        BufferedImage nbi = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);
        //对图像每个像素点进行灰度值与阈值判断，大于阈值的RGB均设为255，小于阈值的RGB均设为0
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                if (getAverageColor(gray, x, y, w, h) > SW) {
                    int max = new Color(255, 255, 255).getRGB();
                    nbi.setRGB(x, y, max);
                } else {
                    num++;
                    int min = new Color(0, 0, 0).getRGB();
                    totalTarget.add(new Target(num, x, y));
                    nbi.setRGB(x, y, min);
                }
                //System.out.println("二进制图像坐标：" + "(" + x + "," + y + ")");
            }
        }

        //获取目标的数量
        System.out.println("采样点数量为：" + totalTarget.size());

        //打印目标坐标
        for (Target target:totalTarget){
            System.out.println(target);
        }

        //绘制二值图像保存到本地
        ImageIO.write(nbi, "jpg", new File(alterPath));
    }

    //通过计算每个像素点的RGB值取平均值作为该像素点的灰度值
    public static int getGray(int rgb) {
        String str = Integer.toHexString(rgb);
        Color c = new Color(rgb);
        int r = c.getRed();
        int g = c.getGreen();
        int b = c.getBlue();
        int top = (r + g + b) / 3;
        return top;
    }

    //自己加周围8个灰度值再除以9，算出其相对灰度值
    public static int getAverageColor(int[][] gray, int x, int y, int w, int h) {
        int rs = gray[x][y]
                + (x == 0 ? 255 : gray[x - 1][y])
                + (x == 0 || y == 0 ? 255 : gray[x - 1][y - 1])
                + (x == 0 || y == h - 1 ? 255 : gray[x - 1][y + 1])
                + (y == 0 ? 255 : gray[x][y - 1])
                + (y == h - 1 ? 255 : gray[x][y + 1])
                + (x == w - 1 ? 255 : gray[x + 1][y])
                + (x == w - 1 || y == 0 ? 255 : gray[x + 1][y - 1])
                + (x == w - 1 || y == h - 1 ? 255 : gray[x + 1][y + 1]);
        return rs / 9;
    }

    //根据保存的坐标点用java绘制
    /**
     * 放大图片
     *
     * @param g2d Graphics2D参数
     * @param s   需要绘制的对象
     */
    public static void draw(Graphics2D g2d, ArrayList<Target> target, Model.TransFormModel.Sampling2 s) {
        //将保存的坐标点绘制
        for (int i = 0; i < target.size(); i++) {
            //System.out.println("二进制图像坐标：" + "(" + ships.get(i).x + "," + ships.get(i).y + ")");
            g2d.draw(new Line2D.Double(target.get(i).x * 2 + s.MOVE, target.get(i).y * 2 + s.MOVE,
                    target.get(i).x * 2 + s.MOVE + 0.5, target.get(i).y * 2 + s.MOVE + 0.5));
            //g2d.draw(new Ellipse2D.Double(ships.get(i).x+100,ships.get(i).y+100,0.1,0.1));
        }
    }

    //对所有采样点进行顶点过滤
    public void getVertex(String srcPath, String alterPath,Object obj) throws IOException {
        targets.clear();
        targetseconds.clear();
        transfer(srcPath,alterPath);
        ArrayList<Target> target_tmp = new ArrayList<>();
        ArrayList<Distance> arr_dis = new ArrayList<>();
        int nn ;
        int length = totalTarget.size();
        int n = 0;
        double dis = 4; //用来判断采样点的分隔性
        ArrayList<Integer> mark = new ArrayList<Integer>();
        double size = 2; //用于判断“重复性”坐标之间的距离
        int num = 0; //用于给目标编号
        double cosθ = -1;
        double θ = π;
        Random r = new Random();
        while (n<length-1){
            Target target = totalTarget.get(n);
            mark.add(n);
            // 对图像预处理 将采样点中x相同y相距1的点取最开始的一个
            for (int j=n+1;j<length;j++){
                //如果循环次数过多 则退出
                if (j>n+100){
                    n=j;
                    break;
                }
                else if (totalTarget.get(j).x==target.x&& Math.abs(totalTarget.get(j).y-totalTarget.get(j-1).y)< dis &&j != length-1) continue;
                else if (j==length-1){
                    n=j;
                    break;
                }
                else {
                    n=j;
                    break;
                }
            }
        }
        for (int a :mark){
            target_tmp.add(totalTarget.get(a));
        }
        totalTarget.clear();
        totalTarget = target_tmp;
        targets = totalTarget;
        nn = targets.size();
        while(nn>25) {
            arr_dis.clear();
            for (int i = 0; i < targets.size(); i++) {
                Target target = targets.get(i);
                for (int j = i + 1; j < targets.size(); j++) {
                    Target t = targets.get(j);
                    Double distance = Target.distance(target.x, target.y, t.x, t.y);
                    arr_dis.add(new Distance(i, j, distance));
                }
            }
            Collections.sort(arr_dis, new Comparator<Distance>() {
                @Override
                public int compare(Distance o1, Distance o2) {
                    double a = o1.dis - o2.dis;
                    if (a > 0) return 1;
                    else if (a == 0) return 0;
                    else return -1;
                }
            });
            System.out.println(arr_dis.get(0).num1 + "-" + arr_dis.get(0).num2 + "-" + arr_dis.get(0).dis);
            if (r.nextInt(2) == 0) {
                targets.remove(arr_dis.get(0).num1);
            }else {
                targets.remove(arr_dis.get(0).num2);
            }
            System.out.println(targets.size());
            nn--;
        }
        num = 0;
        for (Target t : targets){
            num++;
            t.num = num;
        }
        if (obj instanceof TargetSecond){
            for (Target t :targets){
                targetseconds.add(new TargetSecond(t.num,t.x,t.y));
            }
        }
//        System.out.println("targets--------------------------");
        for (Target target:targets){
            System.out.println(target);
        }
    }
}
